package ru.urfc.portal.operations;

import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.Serializer;
import net.sf.saxon.s9api.XsltCompiler;
import net.sf.saxon.s9api.XsltExecutable;
import net.sf.saxon.s9api.XsltTransformer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.urfc.portal.managers.DocumentManager;
import ru.urfc.portal.model.documents.Document;
import ru.urfc.portal.ui.client.DocumentResolverPlain;

import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.xml.bind.JAXB;
import javax.xml.transform.stream.StreamSource;

@Stateless
public class DocumentContentService {
	
	protected final Logger logger = LoggerFactory.getLogger(getClass());
	
	private final XsltCompiler xsltCompiler = new Processor(false).newXsltCompiler();
	
	@EJB
	DocumentManager documentManager;

	private final DocumentResolverPlain documentResolver = new DocumentResolverPlain();

	private final ConcurrentMap<String, XsltExecutable> xsltCache = new ConcurrentHashMap<String, XsltExecutable>();
	
	protected void transformWithXsltExecutable(final Reader in, final Writer out, final XsltExecutable executable) {
		try {
			final XsltTransformer transformer = executable.load();
			transformer.setSource(new StreamSource(in));
			final Serializer serializer = new Serializer();
			serializer.setOutputWriter(out);
			transformer.setDestination(serializer);
			transformer.transform();
		} catch (SaxonApiException e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException(e);
		}
	}
	
	public void htmlContent(long docId, Writer contentWriter) {
		final Document document = documentManager.getDocumentWithSelectedDevices(docId);
		htmlContent(document, contentWriter);
	}
	
	public void htmlContent(Document document, Writer contentWriter) {
		final String typeCode = document.getType().getCode();
		logger.debug("typeCode: {}", typeCode);
		XsltExecutable executable = getXsltExecutable(typeCode);
		Reader in = getContentReader(document);
		transformWithXsltExecutable(in, contentWriter, executable);
	}

	public Reader getContentReader(final Document document) {
		Writer writer = new StringWriter();
		xmlSerializeObject(document, writer);
		return new StringReader(writer.toString());
	}

	protected void xmlSerializeObject(final Object object, Writer writer) {
		JAXB.marshal(object, writer);
	}

	protected XsltExecutable getXsltExecutable(String typeCode) {
		final XsltExecutable xsltExecutable = xsltCache.get(typeCode);
		if (xsltExecutable != null) {
			logger.debug("xsltCache hit: {}", typeCode);
			return xsltExecutable;
		} else {
			logger.debug("xsltCache miss: {}", typeCode);
			final XsltExecutable newXsltExecutable = createXsltExecutable(typeCode);
			xsltCache.putIfAbsent(typeCode, newXsltExecutable);
			return newXsltExecutable;
		}
	}

	protected XsltExecutable createXsltExecutable(String typeCode) {
		final String xsltName = documentResolver.xsltName(typeCode);
		logger.debug("using {}", xsltName);
		final InputStream xsltStream = getClass().getClassLoader().getResourceAsStream(xsltName);
		try {
			return xsltCompiler.compile(new StreamSource(xsltStream));
		} catch (SaxonApiException e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException(e);
		}
	}
	
}
